package Visualization.plot;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.ImageObserver;
import RKUjava.awt.*;
import RKUjava.awt.event.*;
import RKUjava.util.RKUStringUtils;
import java.util.*;

public class DataPlot2D
{

  Object datasupplier;

  Frame theframe;
  Panel thepanel;
  DataPlot2DCanvas thecanvas;
  Button printbutton;
  int plotternumber;
  double ymin, ymax, xmin, xmax;
  int yprecision = 2, xprecision = 2;

  /** Create a dataplotter with a given datasupplier. 
      @param adatasupplier An object that implements the interface DataPlot2DExtraction. 
      @param aplottype How to plot the data.<br>
      0 = Plot from left to right. X-axis index starts a 0 and goes on to the
      length of the extracted data.<br>
      1 = First array is x-axis. This type is usually used in connection
      with plotting of data that varies over some parameter.<br>
      2 = (x,y) plot. The number of data arrays extracted must be even. 
      The arrays are paired and plotted. 
      @param asubplottype Controls how each reading is plotted.<br>
      0 = Plot the data with lines between (trajectory plot).<br>
      1 = Plot the data as points.<br>
  */
  public DataPlot2D(Object adatasupplier, int aplotternumber)
    {
      super();
      String ftitle = "2D dataplotter";

      datasupplier = adatasupplier;
      plotternumber = aplotternumber;

      theframe = new Frame(ftitle);
      theframe.setSize(600,400);
      theframe.setLayout(new GridBagLayout());
      theframe.setBackground(Color.white);
      thepanel = new Panel(new GridBagLayout());
      thepanel.setBackground(Color.white);
      thecanvas = new DataPlot2DCanvas(this);
      thecanvas.setBackground(Color.white);

      printbutton = new Button("Print");
      printbutton.addActionListener(new RKUActionListener(this, null) {
	DataPlot2D curplotter;
	Properties lastprops;
	public void initialize(Object acontainer1, Object acontainer2)
	    {
	      curplotter = (DataPlot2D)acontainer1;
	      lastprops = new Properties();
	    };

	public void actionPerformed(ActionEvent e)
	    {
	      PrintJob curjob;
	      Graphics curgraphics;
	      Dimension jobdim;
	      Dimension canvasdim;

	      curjob = (curplotter.thecanvas.getToolkit()).getPrintJob(curplotter.theframe,"2D dataplot", lastprops);
	      if (curjob!=null) {
		jobdim = curjob.getPageDimension();
		canvasdim = curplotter.thecanvas.getSize();
		curgraphics = curjob.getGraphics();
		curgraphics.translate((jobdim.width - canvasdim.width)/2+10,(jobdim.height - canvasdim.height)/2);
		curplotter.thecanvas.print(curgraphics);
		curgraphics.dispose();
		curjob.end();
	      }
	    }
      });
	      

      AWTutil.constrain(thepanel, thecanvas, 0,0,1,1,GridBagConstraints.BOTH,GridBagConstraints.CENTER,1,1,1,1,1,1);	
      AWTutil.constrain(theframe, thepanel, 0,0,1,1,GridBagConstraints.BOTH,GridBagConstraints.CENTER,1,0.9,1,1,1,1);	
      AWTutil.constrain(theframe, printbutton, 0,1,1,1,GridBagConstraints.NONE,GridBagConstraints.EAST,1,0.1,5,5,5,5);	

      ymin = Double.NEGATIVE_INFINITY;
      ymax = Double.POSITIVE_INFINITY;
      xmin = Double.NEGATIVE_INFINITY;
      xmax = Double.POSITIVE_INFINITY;
    }

  /** Set the lower y value. <br>
      <b>Note:</b> If you set this value the autocompuation of lower y value
      is turned off. To Turn on pass Double.NEGATIVE_INFINITY to the method.
  */
  public void setLowerY(double yminvalue)
    {
      ymin = yminvalue;
    }

  /** Set the upper y value. <br>
      <b>Note:</b> If you set this value the autocompuation of upper y value
      is turned off. To Turn on pass Double.POSITIVE_INFINITY to the method.
  */
  public void setUpperY(double ymaxvalue)
    {
      ymax = ymaxvalue;
    }

  /** Set the lower x value. <br>
      <b>Note:</b> If you set this value the autocompuation of lower x value
      is turned off. To Turn on pass Double.NEGATIVE_INFINITY to the method.
  */
  public void setLowerX(double xminvalue)
    {
      xmin = xminvalue;
    }

  /** Set the upper x value. <br>
      <b>Note:</b> If you set this value the autocompuation of upper x value
      is turned off. To Turn on pass Double.POSITIVE_INFINITY to the method.
  */
  public void setUpperX(double xmaxvalue)
    {
      xmax = xmaxvalue;
    }

  /** Update the plot. The data will be extracted from the datasupplier and 
      replotted. */
  public void updatePlot()
    {
      if (!theframe.isVisible())
	theframe.setVisible(true);

      thecanvas.replot();

    }

  /** Ends the plotting with this plotter. Closes window and cleans up. */
  public void endPlot()
    {
      theframe.setVisible(false);
      theframe.dispose();
      theframe = null;
    };
}
class DataPlot2DCanvas extends Canvas
{
  DataPlot2D curplotter;

  Object[][] curobjectdata;
  double[][] curdoubledata;
  int[][] curplottypes;

  double xmin, ymin, xmax, ymax;

  public DataPlot2DCanvas(DataPlot2D aplotter)
    {
      super();
      curplotter = aplotter;
      imgobs = new ImageObserver() {
	public boolean imageUpdate(Image img, int infoflags, int x, int y, int width, int height) { return false;}
      };
    }

  public synchronized void replot()
    {
      boolean yminauto = false, ymaxauto = false;
      boolean xminauto = false, xmaxauto = false;
      int i, j;
      String curstr;
      double maxlen = 0;

      curobjectdata = ((DataPlot2DExtraction)curplotter.datasupplier).extract2DAsObjects(curplotter.plotternumber);

      curplottypes = ((DataPlot2DExtraction)curplotter.datasupplier).extract2DDataTypes(curplotter.plotternumber);

      xmin = curplotter.xmin;
      xmax = curplotter.xmax;
      ymin = curplotter.ymin;
      ymax = curplotter.ymax;
	
      if (ymin == Double.NEGATIVE_INFINITY) {
	ymin = Double.POSITIVE_INFINITY;
	yminauto = true;
      }
      if (ymax == Double.POSITIVE_INFINITY) {
	ymax = Double.NEGATIVE_INFINITY;
	ymaxauto = true;
      }
	
      if (xmin == Double.NEGATIVE_INFINITY) {
	xmin = Double.POSITIVE_INFINITY;
	xminauto = true;
      }
      if (xmax == Double.POSITIVE_INFINITY) {
	xmax = Double.NEGATIVE_INFINITY;
	xmaxauto = true;
      }
	
      if (curobjectdata!=null) {
	if (curdoubledata == null){
	  curdoubledata = new double[curobjectdata.length][0];
	}
	else {
	  if (curdoubledata.length!=curobjectdata.length || curdoubledata[0].length!= curobjectdata[0].length) {
	    curdoubledata = new double[curobjectdata.length][0];
	  }
	}

	for(i=0;i<curdoubledata.length;i++) {
	  curdoubledata[i] = new double[curobjectdata[i].length];
	  maxlen = Math.max(maxlen,curdoubledata[i].length);

	  switch (curplottypes[i][0]) {
	  case 0:
	    for (j=0;j<curdoubledata[i].length;j++) {
	      curdoubledata[i][j] = (new Double(curobjectdata[i][j].toString())).doubleValue();
	      if (ymin>curdoubledata[i][j] && yminauto)
		ymin = curdoubledata[i][j];
	      
	      if (ymax<curdoubledata[i][j] && ymaxauto)
		ymax = curdoubledata[i][j];
	    }
	    break;
	  case 1:
	    curdoubledata[i+1] = new double[curobjectdata[i+1].length];

	    for (j=0;j<curdoubledata[i].length;j++) {
	      curdoubledata[i][j] = (new Double(curobjectdata[i][j].toString())).doubleValue();
	      curdoubledata[i+1][j] = (new Double(curobjectdata[i+1][j].toString())).doubleValue();
	      if (ymin>curdoubledata[i][j] && yminauto)
		ymin = curdoubledata[i][j];
	      
	      if (ymax<curdoubledata[i][j] && ymaxauto)
		ymax = curdoubledata[i][j];
	    }
	    i++;
	    break;
	  case 2:
	    curdoubledata[i+1] = new double[curobjectdata[i+1].length];
	    for (j=0;j<curdoubledata[i].length;j++) {
	      //	      System.out.println("obj"+i+" "+j+" "+curobjectdata[i].length+" "+curobjectdata[i+1].length);
	      //	      System.out.println("dbl"+i+" "+j+" "+curdoubledata[i].length+" "+curdoubledata[i+1].length);
	      curdoubledata[i][j] = (new Double(curobjectdata[i][j].toString())).doubleValue();	
	      curdoubledata[i+1][j] = (new Double(curobjectdata[i+1][j].toString())).doubleValue();
	      if (xmin>curdoubledata[i][j] && xminauto)
		xmin = curdoubledata[i][j];

	      if (xmax<curdoubledata[i][j] && xmaxauto)
		xmax = curdoubledata[i][j];

	      if (ymin>curdoubledata[i+1][j] && yminauto)
		ymin = curdoubledata[i+1][j];

	      if (ymax<curdoubledata[i+1][j] && ymaxauto)
		ymax = curdoubledata[i+1][j];
	    }
	    i++;
	    break;
	  }
	}

      }
      else {
	curdoubledata = ((DataPlot2DExtraction)curplotter.datasupplier).extract2DAsDoubles(curplotter.plotternumber);

	for(i=0;i<curdoubledata.length;i++) {
	  maxlen = Math.max(maxlen,curdoubledata[i].length);

	  switch (curplottypes[i][0]) {
	  case 0:
	    for (j=0;j<curdoubledata[i].length;j++) {
	      if (ymin>curdoubledata[i][j] && yminauto)
		ymin = curdoubledata[i][j];
	      
	      if (ymax<curdoubledata[i][j] && ymaxauto)
		ymax = curdoubledata[i][j];
	    }
	    break;
	  case 1:
	    for (j=0;j<curdoubledata[i].length;j++) {
	      if (ymin>curdoubledata[i][j] && yminauto)
		ymin = curdoubledata[i][j];
	      
	      if (ymax<curdoubledata[i][j] && ymaxauto)
		ymax = curdoubledata[i][j];
	    }
	    i++;
	    break;
	  case 2:
	    for (j=0;j<curdoubledata[i].length;j++) {
			
	      if (xmin>curdoubledata[i][j] && xminauto)
		xmin = curdoubledata[i][j];

	      if (xmax<curdoubledata[i][j] && xmaxauto)
		xmax = curdoubledata[i][j];

	      if (ymin>curdoubledata[i+1][j] && yminauto)
		ymin = curdoubledata[i+1][j];

	      if (ymax<curdoubledata[i+1][j] && ymaxauto)
		ymax = curdoubledata[i+1][j];
	    }
	    i++;
	    break;
	  }
	}
      }

      if (xmin == Double.POSITIVE_INFINITY)
	xmin = 0;
      if (xmax == Double.NEGATIVE_INFINITY) 
	xmax = maxlen;

      yrange = ymax - ymin;
      xrange = xmax - xmin;

      repaint(100);
    }

  public void repaint()
    {
      this.paint(this.getGraphics());
    };
    
  public void repaint(int tm)
    {
      this.paint(this.getGraphics());
    };
    
    
  Image newimage;
  Graphics ng;
  ImageObserver imgobs;
  Color col_white  = new Color(255,255,255);
  Color col_black  = new Color(0,0,0);
  Color col_blue   = new Color(0,0,255);
  Color col_green  = new Color(0,160,0);
  Color col_red    = new Color(230,0,0);
  Color col_purple = new Color(230,0,230);
  Color col_orange = new Color(230,110,0);
  Color col_yellow = new Color(200,200,0);
  Color col_turkis = new Color(0,200,200);
  Color col_darkblue   = new Color(0,0,120);

  Color col_blueCONF   = new Color(190,190,255);
  Color col_greenCONF  = new Color(180,255,180);
  Color col_redCONF    = new Color(255,170,170);
  Color col_purpleCONF = new Color(255,200,255);
  Color col_orangeCONF = new Color(255,220,150);
  Color col_yellowCONF = new Color(240,240,150);
  Color col_turkisCONF = new Color(150,240,240);
  Color col_darkblueCONF   = new Color(100,100,180);

  Color[] plotcolors = {col_blue, col_red, col_green, col_purple, col_orange, col_yellow, col_turkis, col_darkblue};

  Color[] plotcolorsCONF = {col_blueCONF, col_redCONF, col_greenCONF, col_purpleCONF, col_orangeCONF, col_yellowCONF, col_turkisCONF, col_darkblueCONF};

  int plotULx, plotULy, plotLRx, plotLRy;
  int plotULxadd = 10, plotULyadd = 20;
  int plotLRxsub = 100, plotLRysub = 25;
  Dimension curdim;
  int plotw, ploth;
  int xtics = 10, ytics = 10;
  double yrange, xrange;
  int firstindex;

  int i, j, curx, cury;
  String curstr;
  int lastx, lasty;
  int skips;
  int meany;
  int curcolorindex;

  public synchronized void paint(Graphics g)
    {
      // Make sure we have the data.
      if (curdoubledata== null) {
	replot();
	if (curdoubledata == null)
	  return;
      };

      curdim = this.getSize();
	
      plotw = curdim.width - plotULx - plotLRxsub;
      ploth = curdim.height - plotULy - plotLRysub;
	
      plotULx = plotULxadd+(RKUStringUtils.decimalFormatter(ymax,curplotter.yprecision)).length()*5;
      plotULy = plotULyadd;
      plotLRx = curdim.width - plotLRxsub;
      plotLRy = curdim.height - plotLRysub;

      // g.drawString("Something works!", 100,100);
      // if (true) return;

      // newimage = createImage(curdim.width,curdim.height);
      // ng = newimage.getGraphics();
      ng = g;
      ng.setColor(col_white);
      ng.fillRect(0,0,curdim.width,curdim.height);

      curcolorindex = 0;

      // ****** Plot deviation bars! ***************
      for (i=0;i<curdoubledata.length;i++) {
	//	System.out.println("Fdata["+i+"]="+curdoubledata[i].length);
	switch (curplottypes[i][0]) {
	case 0:
	  break;
	case 1:
	  lastx = plotULx;
	  lasty = plotLRy -  (int)((ploth*(curdoubledata[i][0]-ymin))/yrange);
	  ng.setColor(plotcolorsCONF[curcolorindex%plotcolorsCONF.length]);

	  for (j = 1;j<curdoubledata[i].length;j++) {
	    curx = plotULx +  (int)((j*plotw)/curdoubledata[i].length);
	    cury = plotLRy - (int)((ploth*(curdoubledata[i][j]-ymin))/yrange);

	    if (curdoubledata[i+1][j]>0) {
	      meany = (int)((ploth*(curdoubledata[i+1][j]))/yrange);
	      lasty = Math.max(plotULy,cury-meany);
	      ng.drawLine(curx-2,lasty,curx+2,lasty);
	      lasty = Math.min(plotLRy+2,cury+meany);
	      ng.drawLine(curx-2,lasty,curx+2,lasty);
		//		ng.drawLine(curx,cury-meany,curx,cury+meany);
	    }
	  }
	  i++;
	  break;
	case 2:
	  i++;
	  break;
	}
	curcolorindex++;
      }

      curcolorindex = 0;      
      // ****** Plot the real graphs!! ******************
      for (i=0;i<curdoubledata.length;i++) {
	//	System.out.println("data["+i+"]="+curdoubledata[i].length);
	switch (curplottypes[i][0]) {
	case 0:
	  //	  System.out.println("CASE 0:");
	  lastx = plotULx;
	  lasty = plotLRy -  (int)((ploth*(curdoubledata[i][0]-ymin))/yrange);
	  ng.setColor(plotcolors[curcolorindex%plotcolors.length]);
	  for (j = 1;j<curdoubledata[i].length;j++) {
	    curx = plotULx +  (int)((j*plotw)/curdoubledata[i].length);
	    cury = plotLRy - (int)((ploth*(curdoubledata[i][j]-ymin))/yrange);
	    ng.drawLine(lastx,lasty,curx,cury);
	    lastx = curx;
	    lasty = cury;
	  }
	  break;
	case 1:
	  //	  System.out.println("CASE 1:");
	  lastx = plotULx;
	  lasty = plotLRy -  (int)((ploth*(curdoubledata[i][0]-ymin))/yrange);
	  ng.setColor(plotcolors[curcolorindex%plotcolors.length]);

	  for (j = 1;j<curdoubledata[i].length;j++) {
	    curx = plotULx +  (int)((j*plotw)/curdoubledata[i].length);
	    cury = plotLRy - (int)((ploth*(curdoubledata[i][j]-ymin))/yrange);
	    ng.drawLine(lastx,lasty,curx,cury);

	    /*	    if (j%skips == 0) {
	      if (curdoubledata[i+1][j]>0) {
		meany = (int)((ploth*(curdoubledata[i+1][j]))/yrange);
		ng.drawLine(curx-2,cury+meany,curx+2,cury+meany);
		ng.drawLine(curx-2,cury-meany,curx+2,cury-meany);
		//		ng.drawLine(curx,cury-meany,curx,cury+meany);
	      }
	    }
	    */
	    lastx = curx;
	    lasty = cury;
	  }
	  i++;
	  break;
	case 2:
	  //	  System.out.println("CASE 2:");
	  lastx = plotULx + (int)((plotw*(curdoubledata[i][0]-xmin))/xrange);
	  lasty = plotLRy - (int)((ploth*(curdoubledata[i+1][0]-ymin))/yrange);
	  ng.setColor(plotcolors[curcolorindex%plotcolors.length]);
	  //	  System.out.println("i="+i+"/"+curdoubledata.length+"  jmax="+curdoubledata[i].length+"  jmax2="+curdoubledata[i+1].length);

	  for (j = 1;j<curdoubledata[i].length;j++) {
	    curx = plotULx + (int)((plotw*(curdoubledata[i][j]-xmin))/xrange);
	    cury = plotLRy - (int)((ploth*(curdoubledata[i+1][j]-ymin))/yrange);
	    ng.drawLine(lastx,lasty,curx,cury);
	    lastx = curx;
	    lasty = cury;
	  }
	  ng.setColor(col_black);
	  ng.fillOval(lastx-2,lasty-2, 5, 5);
	  i++;
	  break;
	}
	curcolorindex++;
      }
    
      ng.setFont(new Font("helvetica",Font.PLAIN,10));
      ng.setColor(col_black);
      ng.drawLine(plotULx, plotULy-8, plotULx, plotLRy); 
      ng.drawLine(plotULx, plotLRy, plotLRx+8, plotLRy); 

      for(i=0;i<=ytics;i++) {
	cury = plotLRy-((i*ploth)/ytics);
	ng.drawLine(plotULx-3, cury, plotULx, cury);
	curstr = RKUStringUtils.decimalFormatter(ymin+(i*(ymax-ymin))/ytics,curplotter.yprecision);
	ng.drawString(curstr, plotULx-5*curstr.length()-8, cury+4);
      }

      ng.setColor(col_black);
      for(i=0;i<=xtics;i++) {
	curx = plotULx+((i*plotw)/xtics);
	ng.drawLine(curx, plotLRy, curx, plotLRy+4);
	curstr = RKUStringUtils.decimalFormatter(xmin+(i*(xmax-xmin))/xtics,curplotter.xprecision);
	ng.drawString(curstr, curx-3*curstr.length(), plotLRy+15);
      }

      curcolorindex =0;
      for (i=0;i<curplottypes.length;i++) {
	//	System.out.println("plottypeslen="+curplottypes.length+" data["+i+"]="+curplottypes[i].length+"  data["+i+"][0]="+curplottypes[i][0]);
	ng.setColor(plotcolors[curcolorindex%plotcolors.length]);
	cury = plotULy+10+14*curcolorindex;
	ng.drawLine(plotLRx+10,cury, plotLRx+25, cury);
	ng.setColor(col_black);
	ng.drawString(((DataPlot2DExtraction)curplotter.datasupplier).extract2DName(i, curplotter.plotternumber), plotLRx+30,cury+4);
	

	switch (curplottypes[i][0]) {
	case 0:
	  break;
	case 1:
	  i++;
	  break;
	case 2:
	  i++;
	  break;
	}
	curcolorindex++;
      }    
      //	g.drawImage(newimage, 0, 0, col_white, imgobs);
    }
}
